include([], 

function(){
	
	declare('Matrix2d');
	
	Matrix2d = function(x1,y1,x2,y2){
		this.x1 = x1 || 1;
		this.y1 = y1 || 0;
		this.x2 = x2 || 0;
		this.y2 = y2 || 1;
		this.x3 = 0;
		this.y3 = 0;
	}
	
	Matrix2d.prototype = {
		scale : function(x, y){
			this.x1 = x;
			this.y2 = y;
			return this;
		},
		shear : function(x, y){
			this.x2 = x;
			this.y1 = y;
			return this;
		},
		translate : function(x, y){
			this.x3 += x;
			this.y3 += y;
			return this;
		},	
		setTranslation : function(x,y){
			this.x3 = x;
			this.y3 = y;
			return this;
		},	
		rotate : function(degrees){
			var radians = DEGREES_TO_RADIANS * degrees;
			this.x1 = Math.cos(radians);
			this.x2 = Math.sin(radians);
			this.y1 = -Math.sin(radians);
			this.y2 = Math.cos(radians);
			return this;
		},
		clone : function(store){
			if( !store ) store = new Matrix2d();
			store.x1 = this.x1;
			store.x2 = this.x2;
			store.x3 = this.x3;
			store.y1 = this.y1;
			store.y2 = this.y2;
			store.y3 = this.y3;
			return store;
		},	
		reset : function(matrix){
			this.x1 = matrix.x1;
			this.x2 = matrix.x2;
			this.x3 = matrix.x3;
			this.y1 = matrix.y1;
			this.y2 = matrix.y2;
			this.y3 = matrix.y3;
		},
		/**
		 * apply( vectors[] );
		 * 
		 * apply( vector, store );
		 * 
		 * - applies the matrix to the given vector.
		 */
		apply : function(){
			var points = null;
			var store  = null;
			if( arguments.length == 2 ){
				points = [ arguments[0] ];
				store = arguments[1];
			}else{
				points = arguments[0].meshData();
			}
			for(var index=0; index <points.length;index++){
				var point = points[index];
				var x = point.x;
				var y = point.y;
	
				var s = (store == null ? point : store );
				s.x = (x * this.x1 + y * this.x2 + this.x3);
				s.y = (x * this.y1 + y * this.y2 + this.y3);
			}
			return store;
		},
		applyInvert : function(){
			var points = null;
			var store  = null;
			if( arguments.length == 2 ){
				points = [ arguments[0] ];
				store = arguments[1];
			}else{
				points = arguments[0].meshData();
			}
			for(var index=0; index <points.length;index++){
				var point = points[index];
				var s = (store == null ? point : store );
				var x = s.x - this.x3;
				var y = s.y - this.y3;
				
				var det = (this.x1*this.y2 - this.x2*this.y1);
				s.x = (x * this.y2 - y * this.x2) / det;
				s.y = (y * this.x1 - x * this.y1) / det;
			}
			return store;
		},
		invert : function(store){
			if( !store ) 
				store = new Matrix2d();
			m00 = this.x1;
			m01 = this.x2;
			m02 = this.x3;
			m10 = this.y1;
			m11 = this.y2;
			m12 = this.y3;
			
			var det = (m00*m11 - m10*m01);
			store.x1 = m11 / det;
			store.y1 = -m10 / det;
			store.x2 = -m01 / det;
			store.y2 = m00 / det;
			store.x3 = (m01 * m12 - m11 * m02) / det;
			store.y3 = (m10 * m02 - m00 * m12) / det;
			return store;
		},
		multiply : function(othr){
			this.x1 = (this.x1*othr.x1 + this.x2 *othr.y1 );
			this.x2 = (this.x1*othr.x2 + this.x2 *othr.y2 );
			this.x3 = (this.x1*othr.x3 + this.x2 *othr.y3 + this.x3 * 1);
					
			this.y1 = (this.y1*othr.x1 + this.y2 *othr.y1 );
			this.y2 = (this.y1*othr.x2 + this.y2 *othr.y2 );
			this.y3 = (this.y1*othr.x3 + this.y2 *othr.y3 + this.y3 * 1);
			
			return this;
		}
	};
	Matrix2d.Identity = new Matrix2d(1,0,0,1);
});